/*
 * GLDevice.cpp
 *
 *  Created on: 2009/01/05
 *      Author: Yongshik Cho
 */

#include "GLDevice.h"
#include "../core/Math3D.h"
#include "../core/Matrix4.h"
#include "../core/Polygon2D.h"
#include "../core/Curve2D.h"
#include "../SceneManager.h"
#include "../RenderSystem.h"
#include "../LoggerableManager.h"
#include "../Quadtree.h"
#include "../SystemFontsManager.h"
#include "../RenderWindow.h"



const Vector2D RenderDevice::_DEFAULT_AXIS_DIRECTION 	= Vector2D(1.0f, -1.0f);

const std::string GLDevice::_GDI_Generic					= "GDI Generic";
const std::string GLDevice::_GL_EXT_blend_color				= "GL_EXT_blend_color";
const std::string GLDevice::_GL_EXT_blend_func_separate		= "GL_EXT_blend_func_separate";
const std::string GLDevice::_GL_EXT_blend_equation			= "GL_EXT_blend_equation";
const std::string GLDevice::_GL_EXT_blend_minmax			= "GL_EXT_blend_minmax";
const std::string GLDevice::_GL_EXT_blend_subtract			= "GL_EXT_blend_subtract";


GLDevice::GLDevice(const HWND hWnd, const Rect2D& rect, DeviceSettings* sets) :
	RenderDevice(rect, sets)
{
	// TODO Auto-generated constructor stub
	mInitialized = false;
	mWnd = hWnd;

	init();
	setup();
}

GLDevice::~GLDevice()
{
	// TODO Auto-generated destructor stub
	if(mGLRC)
	{
		wglDeleteContext(mGLRC);
		mGLRC = 0;
	}

	AltoLog("GLDevice has been released.");
}

void GLDevice::init()
{
	wglMakeCurrent(0, 0);

	RenderWindow* rw = RenderWindow::GetInstancePtr();
	assert(rw);

	mDC = rw->getDC();
	DeviceCaps dc = rw->getCaps();

	mBitsPerPixel = dc.bitspixel;

	// Create an OpenGL pixel format descriptor
	PIXELFORMATDESCRIPTOR pfd =						// pfd Tells Windows How We Want Things To Be
	{
		sizeof (PIXELFORMATDESCRIPTOR),				// Size Of This Pixel Format Descriptor
			1,										// Version Number
			PFD_DRAW_TO_WINDOW |					// Format Must Support Window
			PFD_SUPPORT_OPENGL |					// Format Must Support OpenGL
			PFD_DOUBLEBUFFER,						// Must Support Double Buffering
			PFD_TYPE_RGBA,							// Request An RGBA Format
			mBitsPerPixel,							// Select Our Color Depth (same as desktop)
			0, 0, 0, 0, 0, 0,						// Color Bits Ignored
			mAlphaBuffer,							// Alpha Buffer
			0,										// Shift Bit Ignored
			0,										// No Accumulation Buffer
			0, 0, 0, 0,								// Accumulation Bits Ignored
			mBitsDepth,								// 16Bit Z-Buffer (Depth Buffer)
			mBitsStencil,							// No Stencil Buffer
			0,										// No Auxiliary Buffer
			PFD_MAIN_PLANE,							// Main Drawing Layer
			0,										// Reserved
			0, 0, 0									// Layer Masks Ignored
	};

	mPixelFormat = ChoosePixelFormat(mDC, &pfd);
	if(!mPixelFormat)
		AltoThrow(Exception::ERR_RENDERING_API);

	if(SetPixelFormat(mDC, mPixelFormat, &pfd) == false)
		AltoThrow(Exception::ERR_RENDERING_API);

	mGLRC = wglCreateContext(mDC);

	wglMakeCurrent(mDC, mGLRC);

	GLenum err = glewInit();
	if(err != GL_NO_ERROR)
		AltoThrowDetail(Exception::ERR_RENDERING_API, _T("Failed to initialize OpenGL."), err);

	mVendor = ((char *)glGetString(GL_VENDOR));
	mVersion = ((char *)glGetString(GL_VERSION));
	mRenderer = ((char *)glGetString(GL_RENDERER));
	//retrieves gl extensions
	mExtensions = (char*)glGetString(GL_EXTENSIONS);

	mSettings->vendor = mVendor;
	mSettings->version = mVersion;
	mSettings->renderer = mRenderer;

	//check device caps
	checkCaps();

	//enumerate display modes
	enumDisplayModes();

	//retrieve log output stream
	Loggerable* log = LoggerableManager::GetInstance().get();

	if(log)
	{
#ifdef UNICODE
		wofstream& out = log->getStream();
		TCHAR wstr[512]; memset(wstr, 0, sizeof(wstr));
#else
		ofstream& out = log->getStream();
#endif

		out << std::endl;

#ifdef UNICODE
		::MultiByteToWideChar(CP_UTF8, 0, mVendor.c_str(), (int32)mVendor.size(), wstr, 512);
		out << _T("Vender : ") << *wstr  << std::endl;
		::MultiByteToWideChar(CP_UTF8, 0, mVersion.c_str(), (int32)mVersion.size(), wstr, 512);
		out << _T("Version : ") << *wstr  << std::endl;
		::MultiByteToWideChar(CP_UTF8, 0, mRenderer.c_str(), (int32)mRenderer.size(), wstr, 512);
		out << _T("Renderer : ") << *wstr  << std::endl;
		::MultiByteToWideChar(CP_UTF8, 0, mExtensions.c_str(), (int32)mExtensions.size(), wstr, 512);
		out << _T("Extensions : ") << *wstr  << std::endl;
#else
		out << _T("Vender : ") << mVendor << std::endl;
		out << _T("Version : ") << mVersion << std::endl;
		out << _T("Renderer : ") << mRenderer << std::endl;
		out << _T("Extensions : ") << mExtensions << std::endl;
#endif

		out << std::endl;
		AltoLog("GLDevice has been initialized.");

		//logging device display modes
		out << std::endl;
		AltoLog(_T("GLDevice display modes"));
		for(vector<VideoCaps>::iterator it=mVideoCapsList.begin();it!=mVideoCapsList.end();++it)
		{
			VideoCaps& caps = *it;
			out << _T("color : ") << caps.color << std::endl;
			out << _T("alpha : ") << caps.alpha << std::endl;
			out << _T("zBuffer : ") << caps.zBuffer << std::endl;
			out << _T("accum : ") << caps.accum << std::endl;
			out << _T("stencil : ") << caps.stencil << std::endl;
			out << _T("aaSamples : ") << caps.aaSamples << std::endl;
			out << _T("hwAcc : ") << caps.hwAcc << std::endl;
			out << _T("sampleBuffer : ") << caps.sampleBuffer << std::endl;
			out << _T("doubleBuffer : ") << caps.doubleBuffer << std::endl;
		}
	}

	mInitialized = true;
}

void GLDevice::setup()
{
	if(!mInitialized)
		return;

	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glPixelStorei(GL_PACK_ALIGNMENT, 1);
	glPixelStorei(GL_PACK_SWAP_BYTES, false);
	glPixelStorei(GL_PACK_LSB_FIRST, false);

	glShadeModel(GL_SMOOTH);

	GLenum err = glGetError();
	if(err != GL_NO_ERROR)
		AltoThrowDetail(Exception::ERR_RENDERING_API, _T("Failed to initialize OpenGL."), err);
}

void GLDevice::checkCaps()
{
	float version = (float)atof(mVersion.c_str());

	mIsSupportedOGL20 = (version >= 2.0);
	if (mIsSupportedOGL20)
		mIsSupportedNPOT = true;
	else
		mIsSupportedNPOT = glewIsSupported("GL_ARB_texture_non_power_of_two") == GL_TRUE ? true : false;

	mIsSupportedFragProg = glewIsSupported("GL_ARB_fragment_program") == GL_TRUE ? true : false;
	mIsSupportedVertexProg = glewIsSupported("GL_ARB_vertex_program") == GL_TRUE ? true : false;
	mIsSupportedGLSL = glewIsSupported("GL_ARB_shading_language_100") == GL_TRUE ? true : false;
	mIsSupportedDrawRangeElements = glewIsSupported("GL_EXT_draw_range_elements") == GL_TRUE ? true : false;
	mIsSupportedMultiTex = glewIsSupported("GL_ARB_multitexture") == GL_TRUE ? true : false;
	mIsSupportedVBO = glewIsSupported("GL_ARB_vertex_buffer_object") == GL_TRUE ? true : false;
	mIsSupportedCompression = glewIsSupported("GL_ARB_texture_compression GL_ARB_texture_cube_map GL_EXT_texture_compression_s3tc") == GL_TRUE ? true : false;
	mIsSupportedPointSprites = glewIsSupported("GL_ARB_point_sprite GL_ARB_point_parameters") == GL_TRUE ? true : false;
	mIsSupportedPBO = wglewIsSupported("WGL_ARB_pbuffer WGL_ARB_render_texture") == GL_TRUE ? true : false;
	mIsSupportedFBO = glewIsSupported("GL_EXT_framebuffer_object") == GL_TRUE ? true : false;
	mIsSupportedAntiAlias = wglewIsSupported("WGL_ARB_multisample") == GL_TRUE ? true : false;
	mIsSupportedWGLPixelFormat = wglewIsSupported("WGL_ARB_pixel_format") == GL_TRUE ? true : false;
	mIsSupportedTexRects = glewIsSupported("GL_ARB_texture_rectangle") == GL_TRUE ? true : false;
	mIsSupportedImaging = glewIsSupported("GL_ARB_imaging") == GL_TRUE ? true : false;
	mIsSupportedTexRects = glewIsSupported("GL_ARB_texture_rectangle") == GL_TRUE ? true : false;
	mIsSupportedMultisample = glewIsSupported("GL_ARB_multisample") == GL_TRUE ? true : false;

	// Now output and log the info
	if(mRenderer == _GDI_Generic)
		mHasHardware = false;
	else
		mHasHardware = true;

	// Rectangle
	if(mIsSupportedTexRects)
		glGetIntegerv(GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB, &mSupportedTextureSize);
	else
		glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mSupportedTextureSize);

	if(!mIsSupportedImaging)
	{
		//checks extentions for the color blending
		if(mExtensions.find(_GL_EXT_blend_color) != std::string::npos)
			mIsSupportedBlendColorEXT = true;
		else
			mIsSupportedBlendColorEXT = false;

		if(mExtensions.find(_GL_EXT_blend_func_separate) != std::string::npos)
			mIsSupportedBlendFuncSeparateEXT = true;
		else
			mIsSupportedBlendFuncSeparateEXT = false;

		if(mExtensions.find(_GL_EXT_blend_equation) != std::string::npos)
			mIsSupportedBlendEquationEXT = true;
		else
			mIsSupportedBlendEquationEXT = false;

		if(mExtensions.find(_GL_EXT_blend_minmax) != std::string::npos)
			mIsSupportedBlendMinMaxEXT = true;
		else
			mIsSupportedBlendMinMaxEXT = false;

		if(mExtensions.find(_GL_EXT_blend_subtract) != std::string::npos)
			mIsSupportedBlendSubtractEXT = true;
		else
			mIsSupportedBlendSubtractEXT = false;
	}
}

void GLDevice::onSize(const uint8 type, const Vector2D& size)
{
	if(!mInitialized)
		return;

	setCurrent();

	glViewport(0, 0, (GLsizei)size.x, (GLsizei)size.y);
	setViewport(Viewport((uint32)size.x, (uint32)size.y));
}

void GLDevice::setCurrent()
{
	if(!wglMakeCurrent(mDC, mGLRC))
		AltoThrow(Exception::ERR_RENDERING_API);
}

void GLDevice::enumDisplayModes()
{
	if(!mDC || !mIsSupportedWGLPixelFormat)
		return;

	int32 modeCount = 0;
	int32 format = WGL_NUMBER_PIXEL_FORMATS_ARB;

	wglGetPixelFormatAttribivARB(mDC, 0, 0, 1, &format, &modeCount);

	if(!modeCount)
		mHasHardware = false;

	vector<int32> attributes;
	vector<int32> results;

	attributes.push_back(WGL_DRAW_TO_WINDOW_ARB);
	attributes.push_back(WGL_STENCIL_BITS_ARB);
	attributes.push_back(WGL_ACCELERATION_ARB);
	attributes.push_back(WGL_DEPTH_BITS_ARB);
	attributes.push_back(WGL_SUPPORT_OPENGL_ARB);
	attributes.push_back(WGL_DOUBLE_BUFFER_ARB);
	attributes.push_back(WGL_PIXEL_TYPE_ARB);
	attributes.push_back(WGL_COLOR_BITS_ARB);
	attributes.push_back(WGL_ALPHA_BITS_ARB);
	attributes.push_back(WGL_ACCUM_BITS_ARB);

	if(mIsSupportedMultisample)
	{
		attributes.push_back(WGL_SAMPLE_BUFFERS_ARB);
		attributes.push_back(WGL_SAMPLES_ARB);
	}

	results.resize(attributes.size());

	for(int32 i=0;i<modeCount;++i)
	{
		if(!wglGetPixelFormatAttribivARB(mDC, i+1, 0, (uint32)attributes.size(), &attributes[0], &results[0]))
			return;

		if((results[0] == GL_TRUE) && (results[4] == GL_TRUE) && (results[3] > 0))
		{
			// what we have here is a contect which is drawable to a window, is in rgba format and is accelerated in some way
			// so now we pull the infomation, fill a structure and shove it into our map
			VideoCaps caps;

			if(mIsSupportedMultisample)
			{
				caps.sampleBuffer = results[10];
				caps.aaSamples = results[11];
			}
			else
			{
				caps.sampleBuffer = false;
				caps.aaSamples = 0;
			}

			caps.accum = results[9];
			caps.alpha = results[8];
			caps.color = results[7];
			//caps.pixelType = results[6]; /* WGL_TYPE_RGBA_ARB*/
			caps.doubleBuffer = results[5];
			caps.zBuffer = results[3];
			caps.hwAcc = results[2]; /*WGL_FULL_ACCELERATION_ARB / WGL_GENERIC_ACCELERATION_ARB / WGL_NO_ACCELERATION_ARB;*/
			caps.stencil = results[1];

			mVideoCapsList.push_back(caps);	// insert into the map
		}
	}
}

void GLDevice::swapBuffers()
{
	glFlush();

	::SwapBuffers(mDC);
}

void GLDevice::drawPixels(const uint32 width, const uint32 height, const uint32 format, const uint8* ucbuf)
{
	glDrawPixels(width, height, format, GL_UNSIGNED_BYTE, ucbuf);
}

GLint GLDevice::getInternalFormat()
{
	GLint internalFormat = GL_RGBA;
	switch(mBitsPerPixel)
	{
	case 8:
		internalFormat = GL_RGBA8;
		break;
	case 16:
		internalFormat = GL_RGBA16;
		break;
	case 24:
	case 32:
		internalFormat = GL_RGBA;
		break;
	}

	return internalFormat;
}

Texture* GLDevice::createTexture(const String& p, const uint32 width, const uint32 height,
		const uint32 orgWidth, const uint32 orgHeight, const uint32 rowBytes, const uint32 format, uint8* bitmap, uint8* orgBitmap)
{
	GLuint id = 0;
	glDeleteTextures(1, &id);
	glGenTextures(1, &id);
	glBindTexture(GL_TEXTURE_2D, id);
	glTexImage2D(GL_TEXTURE_2D, 0, getInternalFormat(), width, height, 0, format, GL_UNSIGNED_BYTE, bitmap);

	GLenum err = glGetError();
	if(err != GL_NO_ERROR)
		AltoThrowDetail(Exception::ERR_RENDERING_API, _T("Failed to create image."), err);

	Texture* texture = new Texture(p, id, orgWidth, orgHeight, rowBytes, format, width, height, orgBitmap);
	if(!texture)
		AltoThrow(Exception::ERR_MEMORY);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	return texture;
}


void GLDevice::rebindTexture(Texture* tex, const uint32 format, uint8* bitmap)
{
	if(!tex)
		return;

	GLuint id = 0;
	glDeleteTextures(1, &id);
	glGenTextures(1, &id);
	glBindTexture(GL_TEXTURE_2D, id);
	uint32 width = tex->getScaledWidth(), height = tex->getScaledHeight();
	glTexImage2D(GL_TEXTURE_2D, 0, getInternalFormat(), width, height, 0, format, GL_UNSIGNED_BYTE, bitmap);

	GLenum err = glGetError();
	if(err != GL_NO_ERROR)
		AltoThrowDetail(Exception::ERR_RENDERING_API, _T("Failed to create image."), err);

	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

void GLDevice::clear(const Color& color)
{
	mClearColor = color;

	glClearColor(color.r, color.g, color.b, color.a);
	glClear(GL_COLOR_BUFFER_BIT);
}

void GLDevice::render(Renderable* r)
{
	//draw it's geometry
	//get the instance of RenderDevice
	RenderDevice& rd = RenderDevice::GetInstance();
	Rect2D viewRect = r->getViewRect();
	Rect2D vpRect = mViewport.getRect();

	if(r->getVisible() && vpRect.isPointIn(viewRect))
	{
		//draw geometry
		switch(r->getRenderableType())
		{
		case Renderable::RENDERABLE_BITMAP:
			drawQuadratic(viewRect, r);
			break;
		case Renderable::RENDERABLE_SHAPE:
			drawShape((ShapeObject*)r);
			break;
		case Renderable::RENDERABLE_PARTICLE:
			drawParticle((ParticleObject*)r);
			break;
		case Renderable::RENDERABLE_RIBBON:
			drawRibbon((RibbonObject*)r);
			break;
		case Renderable::RENDERABLE_TEXT:
			drawText((TextObject*)r);
			break;
		}

		//draw rect if needed
		if(r->getVisibleRect())
			drawRect(viewRect, r->getLineColor());

		if(r->getRenderableType() != Renderable::RENDERABLE_CONTAINER)
		{
			//draw control points if needed
			if(r->getVisibleControlPoint())
				drawControlPoint(viewRect);

			//draw center point if needed
			if(r->getVisibleCenter())
				drawCenterPoint(viewRect, r->getCenterType());
	/*
			//draw collision lines if needed
			if(r->getVisibleCollidable())
				drawCollidable(r);

			//draw collision points if needed
			if(r->getVisibleCollidablePoint())
				drawCollidablePoints(r);

			//draw origin point if needed
			if(r->getVisibleOrigin())
				drawOrigin(r);

			//draw attachment points if needed
			if(r->getVisibleAttachments())
				drawAttachments(r);
	*/
		}
	}
}

void GLDevice::drawControlPoint(const Rect2D& r)
{
	//retrieve original rect
	Rect2D ctr = r;
	Vector2D half = Vector2D((float)(mSettings->control_point_size)/2.f, (float)(mSettings->control_point_size)/2.f);

	Vector2D p = ctr.getPoint(Rect2D::CENTER_LT);
	Rect2D rect = Rect2D(Vector2D(p.x - half.x, p.y - half.y), Vector2D(p.x + half.x, p.y - half.y),
		Vector2D(p.x + half.x, p.y + half.y), Vector2D(p.x - half.x, p.y + half.y));
	drawFillRect(rect, mSettings->control_point_color);

	p = ctr.getPoint(Rect2D::CENTER_RT);
	rect = Rect2D(Vector2D(p.x - half.x, p.y - half.y), Vector2D(p.x + half.x, p.y - half.y),
		Vector2D(p.x + half.x, p.y + half.y), Vector2D(p.x - half.x, p.y + half.y));
	drawFillRect(rect, mSettings->control_point_color);

	p = ctr.getPoint(Rect2D::CENTER_RB);
	rect = Rect2D(Vector2D(p.x - half.x, p.y - half.y), Vector2D(p.x + half.x, p.y - half.y),
		Vector2D(p.x + half.x, p.y + half.y), Vector2D(p.x - half.x, p.y + half.y));
	drawFillRect(rect, mSettings->control_point_color);

	p = ctr.getPoint(Rect2D::CENTER_LB);
	rect = Rect2D(Vector2D(p.x - half.x, p.y - half.y), Vector2D(p.x + half.x, p.y - half.y),
		Vector2D(p.x + half.x, p.y + half.y), Vector2D(p.x - half.x, p.y + half.y));
	drawFillRect(rect, mSettings->control_point_color);
}

void GLDevice::drawCenterPoint(const Rect2D& r, const uint8 centerType)
{
	Rect2D ctr = r;
	Vector2D half = Vector2D((float)(mSettings->center_point_size)/2.f, (float)(mSettings->center_point_size)/2.f);
	Vector2D p = ctr.getPoint(centerType);

	Line2D l1(Vector2D(p.x, p.y - half.y - 1.f), Vector2D(p.x, p.y + half.y + 1.f));
	drawLine(l1, mSettings->center_point_color);
	Line2D l2(Vector2D(p.x - half.x - 1.f, p.y), Vector2D(p.x + half.x + 1.f, p.y));
	drawLine(l2, mSettings->center_point_color);

	Rect2D rect(Vector2D(p.x - half.x, p.y - half.y), Vector2D(p.x + half.x, p.y - half.y),
		Vector2D(p.x + half.x, p.y + half.y), Vector2D(p.x - half.x, p.y + half.y));
	drawCircle(rect, mSettings->center_point_color);
}

void GLDevice::drawCollidable(Renderable* r)
{
	if(!r || !r->getCollidableCount())
		return;

	glColor4fv(mSettings->collidable_color.ptr());

	for(uint32 i=0;i<r->getCollidableCount();++i)
	{
		Collidable* col = r->getCollidable(i);
		if(col)
		{
			if(col->getClosed())
				glBegin(GL_LINE_LOOP);
			else
				glBegin(GL_LINE_STRIP);

			col->setLineIndex(0);
			Vector2D* p = col->getNextPoint();
			while(p)
			{
				*p = r->getViewPosition(*p);
				glVertex2fv(p->ptr());
				p = col->getNextPoint();
			}

			glEnd();
		}
	}
}

void GLDevice::drawCollidablePoints(Renderable* r)
{
	if(!r || !r->getCollidableCount())
		return;

	for(uint32 i=0;i<r->getCollidableCount();++i)
	{
		Collidable* col = r->getCollidable(i);
		if(col)
		{
			col->setLineIndex(0);
			Vector2D* p = col->getNextPoint();
			while(p)
			{
				*p = r->getViewPosition(*p);
				Rect2D rect((float)mSettings->collidable_point_size, (float)mSettings->collidable_point_size);
				rect.setCenterType(Rect2D::CENTER_CM);
				rect.setPosition(*p);
				drawFillRect(rect, mSettings->collidable_point_color);
				p = col->getNextPoint();
			}

			glEnd();
		}
	}
}

void GLDevice::drawOrigin(Renderable* r)
{
	if(!r)
		return;

	Vector2D p = r->getOrigin();
	Rect2D rect((float)mSettings->origin_point_size, (float)mSettings->origin_point_size);
	rect.setCenterType(Rect2D::CENTER_CM);
	p = r->getViewPosition(p);
	drawFillRect(rect, mSettings->center_point_color);
}

void GLDevice::drawAttachments(Renderable* r)
{
	if(!r || !r->getAttachmentCount())
		return;

	for(uint32 i=0;i<r->getAttachmentCount();++i)
	{
		Vector2D p = r->getAttachmentByIndex(i);

		Rect2D rect((float)mSettings->attachment_point_size, (float)mSettings->attachment_point_size);
		rect.setCenterType(Rect2D::CENTER_CM);
		p = r->getViewPosition(p);
		drawFillRect(rect, mSettings->attachment_point_color);
	}
}

void GLDevice::disposeTexture(Texture* texture)
{
	if(!texture)
		return;

	GLuint id = texture->getId();
	glDeleteTextures(1, &id);
}

void GLDevice::cleanupTextures()
{
	GLuint id = 0;
	glGenTextures(1, &id);
	glDeleteTextures(id, &id);
}

void GLDevice::drawQuadratic(const Rect2D& viewRect, Renderable* r)
{
	Texture* tex = r->getTexture();
	if(!tex)
		return;

	//OpenGL settings to draw
	glEnable(GL_CULL_FACE);
	glFrontFace(GL_CW);
	glCullFace(GL_BACK);

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, tex->getId());
	glPolygonMode(GL_FRONT, GL_FILL);

	uint8 blendType = r->getBlendType();
	if(blendType)
	{
		enableByBlendType(blendType);
	}

	//set blending color
	Color blendColor = r->getBlendColor();
	glColor4fv(blendColor.ptr());

	//get rectangle from renderable
	Rect2D rect = getDeviceRect(viewRect);

	//texture coordinates
	Rect2D texCoords = r->getTextureViewRect();

	glBegin(GL_QUADS);

		glTexCoord2fv(texCoords.getPoint(Rect2D::CENTER_LT).ptr());
		glVertex2fv(rect.getPoint(Rect2D::CENTER_LT).ptr());	//LT
		glTexCoord2fv(texCoords.getPoint(Rect2D::CENTER_RT).ptr());
		glVertex2fv(rect.getPoint(Rect2D::CENTER_RT).ptr());	//RT
		glTexCoord2fv(texCoords.getPoint(Rect2D::CENTER_RB).ptr());
		glVertex2fv(rect.getPoint(Rect2D::CENTER_RB).ptr());	//RB
		glTexCoord2fv(texCoords.getPoint(Rect2D::CENTER_LB).ptr());
		glVertex2fv(rect.getPoint(Rect2D::CENTER_LB).ptr());	//LB

	glEnd();

	if(blendType)
	{
		disableByBlendType(blendType);
	}

	glDisable(GL_TEXTURE_2D);
    glDisable(GL_CULL_FACE);
}

void GLDevice::drawShape(ShapeObject* shape)
{
	if(!shape || (shape->getNodeCount()<2))
		return;

	Curve2D curve;
	curve.setType(shape->getType());
	curve.setClosed(shape->getClosed());
	for(uint32 i=0;i<shape->getNodeCount();++i)
	{
		if(shape->getNode(i))
		{
			BezierNode node = *(shape->getNode(i));

			node.position = shape->getViewPosition(node.position);
			node.rotation += shape->getRotation();
			curve.addNode(node);
		}
	}

	switch(shape->getFillStyle())
	{
	case ShapeObject::FILL_SOLID:
		drawFillBezier(curve, shape->getFillColor(), Curve2D::_DEFAULT_STEP_COUNT);
		break;
	}

	switch(shape->getLineStyle())
	{
	case ShapeObject::LINE_SOLID:
		//glLineWidth(shape->getLineWidth());
		drawBezier(curve, shape->getLineColor(), Curve2D::_DEFAULT_STEP_COUNT);
		break;
	}
}

void GLDevice::drawParticle(ParticleObject* particle)
{
	if(!particle || !particle->getParticleCount())
		return;

	uint32 particleCount = particle->getParticleCount();

	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	glEnable(GL_BLEND);

	Texture* tex =particle->getTexture();
	if(tex)
	{
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, tex->getId());
	}

	if(particle->getBlendAdditive())
		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	else
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	Vector2D vertices[4];
	Vector2D coords[4];

	coords[0] = Vector2D(0.f, 1.f);
	coords[1] = Vector2D(1.f, 1.f);
	coords[2] = Vector2D(0.f, 0.f);
	coords[3] = Vector2D(1.f, 0.f);

	Rect2D rect = particle->getRect();
	Vector2D org = rect.getRuledRectPoint(Rect2D::CENTER_CM);

	for(uint32 i=0;i<particleCount;++i)
	{
		Particle* p = particle->getParticle(i);
		if(p)
		{
			Vector2D pos = getDevicePosition(p->getPosition() + org);
			vertices[0] = Vector2D(pos.x - p->getSize() * 0.5f, pos.y - p->getSize() * 0.5f);
			vertices[1] = Vector2D(pos.x + p->getSize() * 0.5f, pos.y - p->getSize() * 0.5f);
			vertices[2] = Vector2D(pos.x - p->getSize() * 0.5f, pos.y + p->getSize() * 0.5f);
			vertices[3] = Vector2D(pos.x + p->getSize() * 0.5f, pos.y + p->getSize() * 0.5f);

			Color color = p->getColor();
			glColor4fv(color.ptr());
			glVertexPointer(2, GL_FLOAT, 0, vertices);
			glTexCoordPointer(2, GL_FLOAT, 0, coords);
			glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
		}
	}

	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	glDisable(GL_BLEND);

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_TEXTURE_COORD_ARRAY);

	if(tex)
	{
		glDisable(GL_TEXTURE_2D);
	}
}

void GLDevice::drawRibbon(RibbonObject* ribbon)
{
	if(!ribbon || !ribbon->getRibbonCount())
		return;

	glBindTexture(GL_TEXTURE_2D, ribbon->getTexture());
	glEnable(GL_BLEND);
	glDisable(GL_LIGHTING);
	glDisable(GL_ALPHA_TEST);
	glDisable(GL_CULL_FACE);
	glDepthMask(GL_FALSE);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE);
	glColor4fv(ribbon->getTcolor().ptr());

	Vector3D* verts = new Vector3D[ribbon->getRibbonCount() * 2];
	Vector2D* texs = new Vector2D[ribbon->getRibbonCount() * 2];

	list<Ribbon>& ribbons = ribbon->getRibbons();
	list<Ribbon>::iterator it = ribbons.begin();
	float l = 0;
	int i = 0;
	for (; it != ribbons.end(); ++it)
	{
        float u = l/ribbon->getLength();

		texs[i] = Vector2D(u, 0);
		verts[i] = it->getPosition() + ribbon->getTabove() * it->getUp();
		i++;

		texs[i] = Vector2D(u, 1);
		verts[i] = it->getPosition() - ribbon->getTbelow() * it->getUp();
		i++;

		l += it->getLength();
	}

	if(ribbons.size() > 1)
	{
		// last segment...?
		--it;

		texs[i] = Vector2D(1, 0);
		verts[i] = it->getPosition() + ribbon->getTabove() * it->getUp() + (it->getLength()/it->getLength0()) * it->getBack();
		i++;

		texs[i] = Vector2D(1, 1);
		verts[i] = it->getPosition() - ribbon->getTabove() * it->getUp() + (it->getLength()/it->getLength0()) * it->getBack();
		i++;
	}

	drawQuadStrip(verts, texs, ribbons.size() * 2);

	glColor4f(1,1,1,1);
	glEnable(GL_LIGHTING);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDepthMask(GL_TRUE);
}

void GLDevice::drawText(TextObject* text)
{
	if(!text)
		return;

	switch(text->getTextStyle())
	{
	case TextObject::TEXT_BITMAP:
		drawQuadratic(text->getViewRect(), text);
		break;
	case TextObject::TEXT_OUTLINE:
		drawOutlineText(text);
		break;
	}
}

void GLDevice::drawOutlineText(TextObject* text)
{
	String str = text->getText();

	const MAT2 mat2 = {{0,1}, {0,0}, {0,0}, {0,1}};
	GLYPHMETRICS gm;
	int8* glyphBuffer;
	uint32 dwSize = 0, wCode;
	Vector2D trans(0.f, 0.f);

	TEXTMETRIC tm;
	::GetTextMetrics(mDC, &tm);

	for(String::iterator it=str.begin();it!=str.end();++it)
	{
		wCode = (uint32)(*it);

		dwSize = ::GetGlyphOutline(mDC, wCode, GGO_NATIVE, &gm, 0, 0, &mat2);
		if(dwSize && dwSize != (uint32)(-1))
		{
			glyphBuffer = new int8[dwSize];
			if(!glyphBuffer)
				AltoThrow(Exception::ERR_MEMORY);

			::GetGlyphOutline(mDC, wCode, GGO_NATIVE, &gm, dwSize, glyphBuffer, &mat2);

			trans.y = (float)tm.tmHeight;

			if(text->getLineStyle() == TextObject::LINE_SOLID)
			{
				glColor4fv(((Color)(text->getLineColor())).ptr());
				glPolygonMode(GL_FRONT, GL_LINE);
				drawOutline_Text(text, trans, (TTPOLYGONHEADER*)glyphBuffer, dwSize);
			}

			SAFE_DELETE_ARRAY(glyphBuffer);
		}

		trans.x += (float)gm.gmCellIncX;
	}
}

void GLDevice::drawOutline_Text(TextObject* text, const Vector2D trans, TTPOLYGONHEADER* header, uint32 size)
{
	TTPOLYGONHEADER* start;
	start = header;

	bool started = false;
	Vector2D p1, p2, p3, p;
	uint32 i = 0, stepCount = TextObject::_DEFAULT_STEP_COUNT;

	while((char *)header < (char *)start + size)
	{
		TTPOLYCURVE *curve;

		if(header->dwType != TT_POLYGON_TYPE)
			return;

		p = Vector2D(float_from_fixed(header->pfxStart.x), -float_from_fixed(header->pfxStart.y));
		p = getDevicePosition(text->getViewPosition(trans + p));

		if(started)
		{
			glEnd(); started = false;
		}

		glBegin(GL_LINE_LOOP); started = true;
		glVertex2fv(p.ptr());

		curve = (TTPOLYCURVE *)(header + 1);

		while((char *)curve < (char *)header + header->cb)
		{
			i = 0;
			switch(curve->wType)
			{
			case TT_PRIM_LINE:
				{
					for(;i<curve->cpfx;i++)
					{
						p = Vector2D(float_from_fixed(curve->apfx[i].x), -float_from_fixed(curve->apfx[i].y));
						p = getDevicePosition(text->getViewPosition(trans + p));
						glVertex2fv(p.ptr());
					}
				}
				break;
			case TT_PRIM_QSPLINE:
			case TT_PRIM_CSPLINE:
				{
					if(!curve->cpfx)
						break;

					POINTFX ptfx;

					ptfx = *(POINTFX *)((char *)curve - sizeof(POINTFX));
					p1 = Vector2D(float_from_fixed(ptfx.x), -float_from_fixed(ptfx.y));
					ptfx = curve->apfx[i++];
					p2 = Vector2D(float_from_fixed(ptfx.x), -float_from_fixed(ptfx.y));

					if(curve->cpfx < 2)
					{
						p2 = getDevicePosition(text->getViewPosition(trans + p2));
						glVertex2fv(p2.ptr());
					}
					else
					{
						for(;i<curve->cpfx;i++)
						{
							ptfx = curve->apfx[i];
							p3 = Vector2D(float_from_fixed(ptfx.x), -float_from_fixed(ptfx.y));
							p3 = (p2 + p3) / 2.f;

							float t = 0.f, step = 1.f / (float)stepCount;
							for(uint32 s=0;s<stepCount;s++)
							{
								p = Curve2D::getQuadraticBezierPoint(p1, p2, p3, t);
								p = getDevicePosition(text->getViewPosition(trans + p));
								glVertex2fv(p.ptr());
								t += step;
							}

							p1 = p2, p2 = p3;
						}
					}
				}
				break;
			} //end of switch(curve->wType)

			curve = (TTPOLYCURVE *)&curve->apfx[curve->cpfx];
		} //end of while((char *)curve < (char *)header + header->cb)

		header = (TTPOLYGONHEADER *)((char *)header + header->cb);
	} //end of while((char *)header < (char *)start + size)

	if(started)
		glEnd();
}

void GLDevice::setupBlendMode(const int32 blendMode)
{
	switch(blendMode)
	{
	case 0:
		glDisable(GL_BLEND);
		glDisable(GL_ALPHA_TEST);
		break;
	case 1:
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_COLOR, GL_ONE);
		glDisable(GL_ALPHA_TEST);
		break;
	case 2:
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
		glDisable(GL_ALPHA_TEST);
		break;
	case 3:
		glDisable(GL_BLEND);
		glDisable(GL_ALPHA_TEST);
		break;
	case 4:
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE);
		glDisable(GL_ALPHA_TEST);
		break;
	}
}

void GLDevice::drawQuad(Vector3D* vert, Vector2D* tex)
{
	glVertexPointer(3, GL_FLOAT, 0, vert);
	glTexCoordPointer(2, GL_FLOAT, 0, tex);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

void GLDevice::drawQuadStrip(Vector3D* vert, Vector2D* tex, size_t size)
{
	glVertexPointer(3, GL_FLOAT, 0, vert);
	glTexCoordPointer(2, GL_FLOAT, 0, tex);
	glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)size);
}

void GLDevice::enableByBlendType(const uint8 type)
{
	switch(type)
	{
	case Color::BLEND_ALPHA:
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		if(mIsSupportedImaging)
			glBlendEquation(GL_FUNC_ADD);
		else if(mIsSupportedBlendEquationEXT && mIsSupportedBlendMinMaxEXT)
			glBlendEquationEXT(GL_FUNC_ADD_EXT);

		break;
	case Color::BLEND_ADD:
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR);

		if(mIsSupportedImaging)
			glBlendEquation(GL_FUNC_ADD);
		else if(mIsSupportedBlendEquationEXT && mIsSupportedBlendMinMaxEXT)
			glBlendEquationEXT(GL_FUNC_ADD_EXT);

		break;
	case Color::BLEND_SUB:
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR);

		if(mIsSupportedImaging)
			glBlendEquation(GL_FUNC_SUBTRACT);
		else if(mIsSupportedBlendEquationEXT && mIsSupportedBlendSubtractEXT)
			glBlendEquationEXT(GL_FUNC_SUBTRACT_EXT);

		break;
	case Color::BLEND_LIGHTEN:
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR);

		if(mIsSupportedImaging)
			glBlendEquation(GL_MAX);
		else if(mIsSupportedBlendEquationEXT && mIsSupportedBlendMinMaxEXT)
			glBlendEquationEXT(GL_MAX_EXT);

		break;
	case Color::BLEND_DARKEN:
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR);

		if(mIsSupportedImaging)
			glBlendEquation(GL_MIN);
		else if(mIsSupportedBlendEquationEXT && mIsSupportedBlendMinMaxEXT)
			glBlendEquationEXT(GL_MIN_EXT);

		break;
	case Color::BLEND_MULTIPLY:
		glEnable(GL_BLEND);
		glBlendFunc(GL_DST_COLOR, GL_ZERO);

		if(mIsSupportedImaging)
			glBlendEquation(GL_FUNC_ADD);
		else if(mIsSupportedBlendEquationEXT && mIsSupportedBlendMinMaxEXT)
			glBlendEquationEXT(GL_FUNC_ADD_EXT);

		break;
	case Color::BLEND_SCREEN:
		glEnable(GL_BLEND);
		glBlendFunc(GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR);

		if(mIsSupportedImaging)
			glBlendEquation(GL_FUNC_ADD);
		else if(mIsSupportedBlendEquationEXT && mIsSupportedBlendMinMaxEXT)
			glBlendEquationEXT(GL_FUNC_ADD_EXT);

		break;
	case Color::BLEND_DODGE:
		glEnable(GL_BLEND);
		glBlendFunc(GL_ONE, GL_SRC_COLOR);

		if(mIsSupportedImaging)
			glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
		else if(mIsSupportedBlendEquationEXT && mIsSupportedBlendSubtractEXT)
			glBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);

		break;
	case Color::BLEND_DIFFERENCE:
		glEnable(GL_COLOR_LOGIC_OP);
		glLogicOp(GL_XOR);
		break;
	case Color::BLEND_INVERSE_DIFFERENCE:
		glEnable(GL_COLOR_LOGIC_OP);
		glLogicOp(GL_EQUIV);
		break;
	case Color::BLEND_INVERT:
		glEnable(GL_COLOR_LOGIC_OP);
		glLogicOp(GL_COPY_INVERTED);
		break;
	}
}

void GLDevice::disableByBlendType(const uint8 type)
{
	switch(type)
	{
	case Color::BLEND_ALPHA:
	case Color::BLEND_ADD:
	case Color::BLEND_SUB:
	case Color::BLEND_LIGHTEN:
	case Color::BLEND_DARKEN:
	case Color::BLEND_MULTIPLY:
	case Color::BLEND_SCREEN:
	case Color::BLEND_DODGE:
		glDisable(GL_BLEND);
		break;
	case Color::BLEND_DIFFERENCE:
	case Color::BLEND_INVERSE_DIFFERENCE:
	case Color::BLEND_INVERT:
		glDisable(GL_COLOR_LOGIC_OP);
		break;
	}
}

const Vector2D GLDevice::getDevicePosition(const Vector2D& p)
{
	return Vector2D(_DEFAULT_AXIS_DIRECTION.x * p.x, _DEFAULT_AXIS_DIRECTION.y * p.y);
}

void GLDevice::drawCenter()
{
	Rect2D rect = mViewport.getRect();

	//size of the view (800, 600)
	//center of the view
	//LT (0, 0)
	//CT (400, 300)
	Vector2D center = getDevicePosition(rect.getRuledRectPoint(Rect2D::CENTER_CM));
	Vector2D horz = Vector2D(0.f, _DEFAULT_AXIS_DIRECTION.x * rect.getWidth());
	Vector2D vert = Vector2D(0.f, _DEFAULT_AXIS_DIRECTION.y * rect.getHeight());

	glPolygonMode(GL_FRONT, GL_LINE);

	glBegin(GL_LINES);
	glColor3f(1.0f, 0.f, 0.f);
	glVertex2f(horz.x, center.y);
	glColor3f(0.0f, 0.f, 1.f);
	glVertex2f(horz.y, center.y);
	glEnd();

	glBegin(GL_LINES);
	glColor3f(0.0f, 0.f, 1.f);
	glVertex2f(center.x, vert.x);
	glColor3f(1.0f, 0.f, 0.f);
	glVertex2f(center.x, vert.y);
	glEnd();
}

void GLDevice::lookAt(const Vector3D& eye, const Vector3D& target, const Vector3D& up)
{
	Vector3D forward, side, _up;

	forward = target - eye;
	forward.normalize();

	side.x = ( forward.y * up.z ) - ( forward.z * up.y );
	side.y = ( forward.z * up.x ) - ( forward.x * up.z );
	side.z = ( forward.x * up.y ) - ( forward.y * up.x );
	side.normalize();

	_up.x = ( side.y * forward.z ) - ( side.z * forward.y );
	_up.y = ( side.z * forward.x ) - ( side.x * forward.z );
	_up.z = ( side.x * forward.y ) - ( side.y * forward.x );

	Matrix4 mat = Matrix4::IDENTITY;

	mat[0][0] = side.x;
	mat[1][0] = side.y;
	mat[2][0] = side.z;

	mat[0][1] = _up.x;
	mat[1][1] = _up.y;
	mat[2][1] = _up.z;

	mat[0][2] = -forward.x;
	mat[1][2] = -forward.y;
	mat[2][2] = -forward.z;

	glMultMatrixf((GLfloat*)&mat);
	glTranslatef(-eye.x, -eye.y, -eye.z);
}

void GLDevice::setPerspective(const float aspect, const float fov, const float nearClip, const float farClip)
{
	float s, cot, dz = farClip - nearClip;
	float rad = Degree(fov * 0.5f).valueRadians();

	s = sinf( rad );

	if (dz == 0 || s == 0 || aspect == 0)
		return;

	cot = cosf( rad ) / s;

	Matrix4 mat = Matrix4::IDENTITY;

	mat[0][0] = cot / aspect;
	mat[1][1] = cot;

	mat[2][2] = -( farClip + nearClip ) / dz;
	mat[2][3] = -1.0f;

	mat[3][2] = -2.0f * farClip * nearClip / dz;
	mat[3][3] = 0.0f;

	glMultMatrixf(mat.ptr());
}

void GLDevice::setOrthogonal(const Camera* camera)
{
	glLoadIdentity();
	//glOrtho(min.x, max.x, min.y, max.y, min.z, max.z);

	SceneManager& sm = SceneManager::GetInstance();
	RenderObjectContainer* root = sm.getRenderRoot();
	Rect2D rect = mViewport.getRect();

	//size of the view (800, 600)
	//center of the view
	//LT (0, 0)
	//CT (400, 300)
	Vector2D center = rect.getRuledRectPoint(root->getCenterType());
	Vector2D min, max;

#ifdef _MSC_VER
	min.x = min(center.x + _DEFAULT_AXIS_DIRECTION.x * mViewport.getWidth(), 0 + _DEFAULT_AXIS_DIRECTION.x * center.x);
	max.x = max(center.x + _DEFAULT_AXIS_DIRECTION.x * mViewport.getWidth(), 0 + _DEFAULT_AXIS_DIRECTION.x * center.x);
	min.y = min(center.y + _DEFAULT_AXIS_DIRECTION.y * mViewport.getHeight(), 0 + _DEFAULT_AXIS_DIRECTION.y * center.y);
	max.y = max(center.y + _DEFAULT_AXIS_DIRECTION.y * mViewport.getHeight(), 0 + _DEFAULT_AXIS_DIRECTION.y * center.y);
#else
	min.x = std::min(center.x + _DEFAULT_AXIS_DIRECTION.x * mViewport.getWidth(), 0 + _DEFAULT_AXIS_DIRECTION.x * center.x);
	max.x = std::max(center.x + _DEFAULT_AXIS_DIRECTION.x * mViewport.getWidth(), 0 + _DEFAULT_AXIS_DIRECTION.x * center.x);
	min.y = std::min(center.y + _DEFAULT_AXIS_DIRECTION.y * mViewport.getHeight(), 0 + _DEFAULT_AXIS_DIRECTION.y * center.y);
	max.y = std::max(center.y + _DEFAULT_AXIS_DIRECTION.y * mViewport.getHeight(), 0 + _DEFAULT_AXIS_DIRECTION.y * center.y);
#endif
	//size of the view (800, 600)
	//min & max of the view
	//LT (0, 800, -600, 0)
	//CT (-400, 400, -300, 300);

	Vector2D scale = camera->getScale();
	min.x /= scale.x;
	max.x /= scale.x;
	min.y /= scale.y;
	max.y /= scale.y;

	Vector3D trans = camera->getPosition();
	min.x += _DEFAULT_AXIS_DIRECTION.x * trans.x;
	max.x += _DEFAULT_AXIS_DIRECTION.x * trans.x;
	min.y += _DEFAULT_AXIS_DIRECTION.y * trans.y;
	max.y += _DEFAULT_AXIS_DIRECTION.y * trans.y;

	glOrtho(min.x, max.x, min.y, max.y, -10000.0, 10000.0);
}

void GLDevice::translate(const Vector2D& t)
{
	glTranslatef(t.x, t.y, 0.f);
}

void GLDevice::setMatrixMode(const uint32 mode)
{
	glMatrixMode(mode);
}

void GLDevice::pushMatrix()
{
	glPushMatrix();
}

void GLDevice::popMatrix()
{
	glPopMatrix();
}

void GLDevice::drawRect(const Rect2D& r, const Color& color)
{
	Rect2D rect = getDeviceRect(r);

	glColor3f(color.r, color.g, color.b);
	glPolygonMode(GL_FRONT, GL_LINE);
	glBegin(GL_LINE_LOOP);
		glVertex2fv(rect.getPoint(Rect2D::CENTER_LT).ptr());
		glVertex2fv(rect.getPoint(Rect2D::CENTER_RT).ptr());
		glVertex2fv(rect.getPoint(Rect2D::CENTER_RB).ptr());
		glVertex2fv(rect.getPoint(Rect2D::CENTER_LB).ptr());
	glEnd();
}

void GLDevice::drawLine(const Line2D& l, const Color& color)
{
	Line2D line = l;

	glColor3f(color.r, color.g, color.b);
	glPolygonMode(GL_FRONT, GL_LINE);
	glBegin(GL_LINES);
		glVertex2fv(getDevicePosition(line.mPoints[0]).ptr());
		glVertex2fv(getDevicePosition(line.mPoints[1]).ptr());
	glEnd();
}

void GLDevice::drawFillRect(const Rect2D& r, const Color& fillColor)
{
	Rect2D rect = getDeviceRect(r);

	glColor3f(fillColor.r, fillColor.g, fillColor.b);
	glPolygonMode(GL_FRONT, GL_FILL);
	glBegin(GL_POLYGON);
		glVertex2fv(rect.getPoint(Rect2D::CENTER_LT).ptr());
		glVertex2fv(rect.getPoint(Rect2D::CENTER_RT).ptr());
		glVertex2fv(rect.getPoint(Rect2D::CENTER_RB).ptr());
		glVertex2fv(rect.getPoint(Rect2D::CENTER_LB).ptr());
	glEnd();
}

void GLDevice::drawPolygon(Polygon2D& poly, const Color& color)
{
	glColor3f(color.r, color.g, color.b);
	glPolygonMode(GL_FRONT, GL_LINE);

	if(poly.getClosed())
		glBegin(GL_LINE_LOOP);
	else
		glBegin(GL_LINE_STRIP);

		Vector2D* p = poly.getNextPoint();
		while(p)
		{
			glVertex2fv(getDevicePosition(*p).ptr());
			p = poly.getNextPoint();
		}
	glEnd();
}

void GLDevice::drawFillPolygon(Polygon2D& poly, const Color& fillColor)
{
	glColor3f(fillColor.r, fillColor.g, fillColor.b);
	glPolygonMode(GL_FRONT, GL_FILL);

	Vector2D* p = poly.getNextPoint();
	glBegin(GL_POLYGON);
		while(p)
		{
			glVertex2fv(getDevicePosition(*p).ptr());
			p = poly.getNextPoint();
		}
	glEnd();
}

void GLDevice::drawBezier(Curve2D& curve, const Color& color, uint32 stepCount)
{
	if(!stepCount)
		return;

	if(curve.getNodeCount() < 2)
		return;

	glColor3f(color.r, color.g, color.b);
	glPolygonMode(GL_FRONT, GL_LINE);

	if(curve.getClosed())
		glBegin(GL_LINE_LOOP);
	else
		glBegin(GL_LINE_STRIP);

	Vector2D p1, p2, p3, p4, p;
	int32 index = 0, dir = -1;
	BezierNode *node = curve.getNode(index++), *next = 0;
	while(1)
	{
		dir = -dir;

		next = curve.getNode(index++);
		if(!next)
			break;

		p1 = node->position;
		p4 = next->position;
		Radian rot = Radian(Math::DegreesToRadians(node->rotation + 90.f * dir));
		p2 = p1 + Vector2D(Math::Cos(rot), Math::Sin(rot)) * node->weight;
        rot = Radian(Math::DegreesToRadians(next->rotation + 90.f * dir));
        p3 = p4 + Vector2D(Math::Cos(rot), Math::Sin(rot)) * next->weight;

		float t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			if(curve.getType() == Curve2D::_LINEAR)
				p = Curve2D::getLinearBezierPoint(p1, p4, t);
			else if(curve.getType() == Curve2D::_BEZIER)
				p = Curve2D::getCubicBezierPoint(p1, p2, p3, p4, t);

			p = getDevicePosition(p);
			glVertex2fv(p.ptr());
			t += step;
		}

		node = next;
	}

	glEnd();
}

void GLDevice::drawPath(Path& path, const Color& color, uint32 stepCount)
{
	if(!stepCount)
		return;

	if(path.getNodeCount() < 2)
		return;

	glColor3f(color.r, color.g, color.b);
	glPolygonMode(GL_FRONT, GL_LINE);

	if(path.getClosed())
		glBegin(GL_LINE_LOOP);
	else
		glBegin(GL_LINE_STRIP);

	Vector2D p1, p2, p3, p4, p;
	int32 index = 0, dir = -1;
	BezierNode *node = path.getNode(index++), *next = 0;
	while(1)
	{
		dir = -dir;

		next = path.getNode(index++);
		if(!next)
			break;

		p1 = node->position;
		p4 = next->position;
		Radian rot = Radian(Math::DegreesToRadians(node->rotation + 90.f * dir));
		p2 = p1 + Vector2D(Math::Cos(rot), Math::Sin(rot)) * node->weight;
        rot = Radian(Math::DegreesToRadians(next->rotation + 90.f * dir));
        p3 = p4 + Vector2D(Math::Cos(rot), Math::Sin(rot)) * next->weight;

		float t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			if(path.getType() == Curve2D::_LINEAR)
				p = Curve2D::getLinearBezierPoint(p1, p4, t);
			else if(path.getType() == Curve2D::_BEZIER)
				p = Curve2D::getCubicBezierPoint(p1, p2, p3, p4, t);

			p = getDevicePosition(p);
			glVertex2fv(p.ptr());
			t += step;
		}

		node = next;
	}

	glEnd();
}

void GLDevice::drawFillBezier(Curve2D& curve, const Color& fillColor, uint32 stepCount)
{
	if(!stepCount)
		return;

	if(curve.getNodeCount() < 2)
		return;

	glColor3f(fillColor.r, fillColor.g, fillColor.b);
	glPolygonMode(GL_FRONT, GL_FILL);

	if(curve.getClosed())
		glBegin(GL_LINE_LOOP);
	else
		glBegin(GL_LINE_STRIP);

	Vector2D p1, p2, p3, p4, p;
	int32 index = 0, dir = -1;
	BezierNode *node = curve.getNode(index++), *next = 0;
	while(1)
	{
		dir = -dir;

		next = curve.getNode(index++);
		if(!next)
			break;

		p1 = node->position;
		p4 = next->position;
		Radian rot = Radian(Math::DegreesToRadians(node->rotation + 90.f * dir));
		p2 = p1 + Vector2D(Math::Cos(rot), Math::Sin(rot)) * node->weight;
        rot = Radian(Math::DegreesToRadians(next->rotation + 90.f * dir));
        p3 = p4 + Vector2D(Math::Cos(rot), Math::Sin(rot)) * next->weight;

		float t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			if(curve.getType() == Curve2D::_LINEAR)
				p = Curve2D::getLinearBezierPoint(p1, p4, t);
			else if(curve.getType() == Curve2D::_BEZIER)
				p = Curve2D::getCubicBezierPoint(p1, p2, p3, p4, t);

			p = getDevicePosition(p);
			glVertex2fv(p.ptr());
			t += step;
		}

		node = next;
	}

	glEnd();
}

void GLDevice::drawCircle(Rect2D& r, Color& color)
{
	uint32 stepCount = 8;
	Vector2D p1, p2, p3, p;

	Rect2D rect = getDeviceRect(r);

	glColor3f(color.r, color.g, color.b);
	glPolygonMode(GL_FRONT, GL_LINE);

	glBegin(GL_LINE_STRIP);

		p1 = rect.getPoint(Rect2D::CENTER_LM);
		p2 = rect.getPoint(Rect2D::CENTER_LT);
		p3 = rect.getPoint(Rect2D::CENTER_CT);
		float t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			p = Curve2D::getQuadraticBezierPoint(p1, p2, p3, t);
			glVertex2fv(p.ptr());
			t += step;
		}

		p1 = rect.getPoint(Rect2D::CENTER_CT);
		p2 = rect.getPoint(Rect2D::CENTER_RT);
		p3 = rect.getPoint(Rect2D::CENTER_RM);
		t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			p = Curve2D::getQuadraticBezierPoint(p1, p2, p3, t);
			glVertex2fv(p.ptr());
			t += step;
		}

		p1 = rect.getPoint(Rect2D::CENTER_RM);
		p2 = rect.getPoint(Rect2D::CENTER_RB);
		p3 = rect.getPoint(Rect2D::CENTER_CB);
		t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			p = Curve2D::getQuadraticBezierPoint(p1, p2, p3, t);
			glVertex2fv(p.ptr());
			t += step;
		}

		p1 = rect.getPoint(Rect2D::CENTER_CB);
		p2 = rect.getPoint(Rect2D::CENTER_LB);
		p3 = rect.getPoint(Rect2D::CENTER_LM);
		t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			p = Curve2D::getQuadraticBezierPoint(p1, p2, p3, t);
			glVertex2fv(p.ptr());
			t += step;
		}

	glEnd();
}

void GLDevice::drawFillCircle(Rect2D& r, Color& fillColor)
{
	uint32 stepCount = 8;
	Vector2D p1, p2, p3, p;

	glColor3f(fillColor.r, fillColor.g, fillColor.b);
	glPolygonMode(GL_FRONT, GL_FILL);

	glBegin(GL_POLYGON);

		p1 = r.getRuledRectPoint(Rect2D::CENTER_LM);
		p2 = r.getRuledRectPoint(Rect2D::CENTER_LT);
		p3 = r.getRuledRectPoint(Rect2D::CENTER_CT);
		float t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			p = Curve2D::getQuadraticBezierPoint(p1, p2, p3, t);
			p = getDevicePosition(p);
			glVertex2fv(p.ptr());
			t += step;
		}

		p1 = r.getRuledRectPoint(Rect2D::CENTER_CT);
		p2 = r.getRuledRectPoint(Rect2D::CENTER_RT);
		p3 = r.getRuledRectPoint(Rect2D::CENTER_RM);
		t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			p = Curve2D::getQuadraticBezierPoint(p1, p2, p3, t);
			p = getDevicePosition(p);
			glVertex2fv(p.ptr());
			t += step;
		}

		p1 = r.getRuledRectPoint(Rect2D::CENTER_RM);
		p2 = r.getRuledRectPoint(Rect2D::CENTER_RB);
		p3 = r.getRuledRectPoint(Rect2D::CENTER_CB);
		t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			p = Curve2D::getQuadraticBezierPoint(p1, p2, p3, t);
			p = getDevicePosition(p);
			glVertex2fv(p.ptr());
			t += step;
		}

		p1 = r.getRuledRectPoint(Rect2D::CENTER_CB);
		p2 = r.getRuledRectPoint(Rect2D::CENTER_LB);
		p3 = r.getRuledRectPoint(Rect2D::CENTER_LM);
		t = 0.f, step = 1.f / (float)stepCount;
		for(uint32 i=0;i<stepCount;i++)
		{
			p = Curve2D::getQuadraticBezierPoint(p1, p2, p3, t);
			p = getDevicePosition(p);
			glVertex2fv(p.ptr());
			t += step;
		}

	glEnd();
}

const Vector3D GLDevice::fixCoordSystem(const Vector3D v)
{
	return Vector3D(v.x, v.z, -v.y);
}

const Quaternion GLDevice::fixCoordSystemQuat(const Quaternion q)
{
	return Quaternion(-q.x, -q.z, q.y, q.w);
}
